/*
 * ESP32 Bluetooth Audio Receiver - Bluetooth Audio Receiver Implementation
 *
 * Developed based on ESP-IDF framework using Espressif official interfaces
 * Core bluetooth processing, audio data callbacks and other components are original development
 *
 * Copyright (c) 2025 lianggumei
 *
 * SPDX-License-Identifier: MIT
 */

#include "bluetooth_audio_receiver.hpp"

#include "bt_app_core.h"

const char* BluetoothAudioReceiver::kTag = "BluetoothAudioReceiver";
const char* BluetoothAudioReceiver::kDeviceName = "ESP32 Bluetooth Speaker";
uint8_t BluetoothAudioReceiver::s_volume_ = 50;
BluetoothAudioReceiver* BluetoothAudioReceiver::instance_ = nullptr;

BluetoothAudioReceiver::BluetoothAudioReceiver() : audio_processor_(nullptr) {
  instance_ = this;
  ESP_LOGI(kTag, "Bluetooth audio receiver initialized");
}

BluetoothAudioReceiver::~BluetoothAudioReceiver() {
  instance_ = nullptr;
  ESP_LOGI(kTag, "Bluetooth audio receiver destroyed");
}

esp_err_t BluetoothAudioReceiver::Initialize() {
  ESP_LOGI(kTag, "Initializing bluetooth audio receiver");
  
  // Initialize bluetooth controller
  esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
  esp_err_t ret = esp_bt_controller_init(&bt_cfg);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Bluetooth controller initialization failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Enable bluetooth controller
  ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Initialize bluetooth stack
  ret = esp_bluedroid_init();
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Bluetooth stack initialization failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Enable bluetooth stack
  ret = esp_bluedroid_enable();
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Bluetooth stack enable failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Register callbacks
  ret = esp_bt_gap_register_callback(BluetoothAudioReceiver::GapCallback);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "GAP callback registration failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  ret = esp_a2d_register_callback(BluetoothAudioReceiver::A2dpCallback);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "A2DP callback registration failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  ret = esp_a2d_sink_register_data_callback(BluetoothAudioReceiver::AudioDataCallback);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "A2DP audio data callback registration failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  ret = esp_avrc_ct_register_callback(BluetoothAudioReceiver::AvrcControllerCallback);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "AVRC controller callback registration failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  ret = esp_avrc_tg_register_callback(BluetoothAudioReceiver::AvrcTargetCallback);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "AVRC target callback registration failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Initialize A2DP sink
  ret = esp_a2d_sink_init();
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "A2DP Sink initialization failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Initialize AVRC controller
  ret = esp_avrc_ct_init();
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "AVRC controller initialization failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Initialize AVRC target
  ret = esp_avrc_tg_init();
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "AVRC target initialization failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Set device name
  ret = esp_bt_gap_set_device_name(kDeviceName);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Set device name failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  // Set discoverable mode
  ret = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
  if (ret != ESP_OK) {
    ESP_LOGE(kTag, "Set scan mode failed: %s", esp_err_to_name(ret));
    return ret;
  }
  
  ESP_LOGI(kTag, "Bluetooth audio receiver initialization successful");
  return ESP_OK;
}

esp_err_t BluetoothAudioReceiver::Start() {
  
  ESP_LOGI(kTag, "Device name: %s", kDeviceName);
  ESP_LOGI(kTag, "Waiting for bluetooth connection...");
  
  return ESP_OK;
}

void BluetoothAudioReceiver::SetAudioProcessor(std::shared_ptr<AudioProcessor> processor) {
  audio_processor_ = processor;
}

// Static callback function implementations
void BluetoothAudioReceiver::GapCallback(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
  switch (event) {
    case ESP_BT_GAP_AUTH_CMPL_EVT:
      if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) {
        ESP_LOGI(kTag, "Authentication successful: %s", param->auth_cmpl.device_name);
      } else {
        ESP_LOGE(kTag, "Authentication failed: %s", esp_err_to_name(param->auth_cmpl.stat));
      }
      break;
    case ESP_BT_GAP_PIN_REQ_EVT:
      ESP_LOGI(kTag, "PIN request");
      break;
    case ESP_BT_GAP_CFM_REQ_EVT:
      ESP_LOGI(kTag, "Confirmation request");
      break;
    case ESP_BT_GAP_KEY_NOTIF_EVT:
      ESP_LOGI(kTag, "Key notification");
      break;
    case ESP_BT_GAP_KEY_REQ_EVT:
      ESP_LOGI(kTag, "Key request");
      break;
    default:
      break;
  }
}

void BluetoothAudioReceiver::A2dpCallback(esp_a2d_cb_event_t event, esp_a2d_cb_param_t *param) {
  switch (event) {
    case ESP_A2D_CONNECTION_STATE_EVT: {
      esp_a2d_connection_state_t state = param->conn_stat.state;
      switch (state) {
        case ESP_A2D_CONNECTION_STATE_DISCONNECTED:
          ESP_LOGI(kTag, "A2DP connection disconnected");
          break;
        case ESP_A2D_CONNECTION_STATE_CONNECTING:
          ESP_LOGI(kTag, "A2DP connecting");
          break;
        case ESP_A2D_CONNECTION_STATE_CONNECTED:
          ESP_LOGI(kTag, "A2DP connection successful");
          break;
        case ESP_A2D_CONNECTION_STATE_DISCONNECTING:
          ESP_LOGI(kTag, "A2DP disconnecting");
          break;
        default:
          break;
      }
      break;
    }
    case ESP_A2D_AUDIO_STATE_EVT: {
      esp_a2d_audio_state_t state = param->audio_stat.state;
      switch (state) {
        case ESP_A2D_AUDIO_STATE_STARTED:
          ESP_LOGI(kTag, "A2DP audio started");
          break;
        case ESP_A2D_AUDIO_STATE_STOPPED:
          ESP_LOGI(kTag, "A2DP audio stopped");
          break;
        default:
          break;
      }
      break;
    }
    case ESP_A2D_AUDIO_CFG_EVT:
      ESP_LOGI(kTag, "A2DP audio configuration event");
      break;
    default:
      break;
  }
}

void BluetoothAudioReceiver::AvrcControllerCallback(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param) {
  switch (event) {
    case ESP_AVRC_CT_CONNECTION_STATE_EVT:
      if (param->conn_stat.connected) {
        ESP_LOGI(kTag, "AVRC controller connected");
      } else {
        ESP_LOGI(kTag, "AVRC controller disconnected");
      }
      break;
    case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
      ESP_LOGI(kTag, "AVRC controller notification changed");
      break;
    case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
      ESP_LOGI(kTag, "AVRC controller remote features");
      break;
    default:
      break;
  }
}

void BluetoothAudioReceiver::AvrcTargetCallback(esp_avrc_tg_cb_event_t event, esp_avrc_tg_cb_param_t *param) {
  switch (event) {
    case ESP_AVRC_TG_CONNECTION_STATE_EVT:
      if (param->conn_stat.connected) {
        ESP_LOGI(kTag, "AVRC target connected");
      } else {
        ESP_LOGI(kTag, "AVRC target disconnected");
      }
      break;
    case ESP_AVRC_TG_PASSTHROUGH_CMD_EVT:
      ESP_LOGI(kTag, "AVRC target passthrough command");
      break;
    case ESP_AVRC_TG_SET_ABSOLUTE_VOLUME_CMD_EVT:
      ESP_LOGI(kTag, "AVRC target set absolute volume: %d", param->set_abs_vol.volume);
      VolumeSetByController(param->set_abs_vol.volume);
      break;
    default:
      break;
  }
}

void BluetoothAudioReceiver::AudioDataCallback(const uint8_t *data, uint32_t len) {
  if (instance_ && instance_->audio_processor_) {
    instance_->audio_processor_->ProcessAudioData(data, len, s_volume_);
  }
}

void BluetoothAudioReceiver::VolumeSetByController(uint8_t volume) {
  s_volume_ = volume;
}
